package com.bdy.lm.taximanager.thread;

import android.content.Context;
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.util.Log;

import com.bdy.lm.taximanager.app.MyApp;
import com.bdy.lm.taximanager.http.HttpJson;
import com.bdy.lm.taximanager.model.CarMeasure;
import com.bdy.lm.taximanager.model.LocationTrack;
import com.bdy.lm.taximanager.model.MessagePackaging;
import com.bdy.lm.taximanager.model.SerialPortData;
import com.bdy.lm.taximanager.model.StandardRoute;
import com.bdy.lm.taximanager.port.TaxiMeterDataAnalysis;
import com.bdy.lm.taximanager.util.JsonUtils;
import com.bdy.lm.taximanager.util.TimeUtils;
import com.google.gson.Gson;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.greenrobot.event.EventBus;

/**
 * Created by liming on 2017/7/10/0010.
 */

public class VerifyAdjustThread implements TaxiMeterDataAnalysis.PassQueryMeterData {

    private SharedPreferences spRoute, spResult;
    private SharedPreferences.Editor editorRoute, editorResult;
    private List<StandardRoute> sRoutes;
    private List<String> standardRouteNums;
    private CarMeasure carMeasure;
    private double startMeterPoint, endMeterPoint, taximeterMeasure = 0;//计价器开始里程点，结束时里程点，相减测量结果，ScarMeasure单位为米
    private HttpJson httpJson;
    private boolean isFirstGetPoint = true;//默认true  为第一次获取里程点
    private DecimalFormat format;
    private List<LocationTrack> locationTracks;
    private Gson gson;
    private StandardRoute suitableRoute = null;
    private static VerifyAdjustThread verifyAdjustThread;
    private double endLng, endLat, radius;
    private boolean isBeginRecordTrack = true;
    private static final String DECIMAL_PRECISION = "#.000000";
    public static final String STANDARD_ROUTE_SP_NAME = "StandardRoute";
    public static final String VERIFY_RESULT_SP_NAME = "VerifyResult", TAG = "VerifyAdjustThread";
    private double timeOut = 0;//十分钟
    private Logger logger;
    private ExecutorService executor;
    int sum = 0;
    private DecimalFormat df = new DecimalFormat("######0.0000");

    public static VerifyAdjustThread getInstance() {
        if (verifyAdjustThread == null) {
            verifyAdjustThread = new VerifyAdjustThread();
            return verifyAdjustThread;
        } else {
            return verifyAdjustThread;
        }
    }

    public VerifyAdjustThread() {
        TaxiMeterDataAnalysis.newInstance().setPassQueryMeterDataCallBack(this);
        spRoute = MyApp.context.getSharedPreferences(STANDARD_ROUTE_SP_NAME, Context.MODE_PRIVATE);
        editorRoute = spRoute.edit();
        spResult = MyApp.context.getSharedPreferences(VERIFY_RESULT_SP_NAME, Context.MODE_PRIVATE);
        editorResult = spResult.edit();
        sRoutes = new ArrayList<>();
        standardRouteNums = new ArrayList<>();
        carMeasure = new CarMeasure();
        httpJson = new HttpJson();
        format = new DecimalFormat(DECIMAL_PRECISION);
        locationTracks = new CopyOnWriteArrayList<>();
        gson = new Gson();
        executor = Executors.newFixedThreadPool(6);
        logger = Logger.getLogger(this.getClass());
        //检测到有上传失败的校准结果，开始上传
        if(spResult.contains(VERIFY_RESULT_SP_NAME)) {
            executor.execute(uploadVerifyResult);
        }
        executor.execute(getStandRoutes);
    }

    //每次登陆初始化时，获取服务器的全部标准路线
    private Runnable getStandRoutes = new Runnable() {
        @Override
        public void run() {
            HttpJson httpJson = new HttpJson();
            while (httpJson.getPostJson() == null) {
                httpJson.HttpJsonPost(JsonUtils.mapToJsonObject(new MessagePackaging(113).getHeartBitMessagePackageMap()), MyApp.serverUrl);
                delay(2000);
            }
            try {
                JSONArray sss = httpJson.getPostJson().getJSONArray("standardRoutes");
                SharedPreferences sp = MyApp.context.getSharedPreferences(STANDARD_ROUTE_SP_NAME, Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = sp.edit();
                for (int i = 0; i < sss.length(); i++) {
                    String j = sss.get(i).toString();
                    StandardRoute s = JsonUtils.gson.fromJson(j, StandardRoute.class);
                    sRoutes.add(s);
                    editor.putString(Integer.toString(s.getNum()), j);
                }
                editor.commit();
                Log.d(TAG, sRoutes.toString());
                executor.execute(checkIsReachRoute);
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }
    };

    /**
     *  登陆后检测到本地有未校准的路线后调用，
     *  查询指令线程收到校准路线后调用
     */
//    public void receiveVerifyNotify() {
//        logger.info("Verify接收到校准通知");
//        EventBus.getDefault().post("接收到校准通知");
//        Log.d("Verify", "接收到校准通知");
//        if (!isVerifying) {
//            isVerifying = true;
//            executor.execute(checkIsReachRoute);
//        } else {
//            waitingVerify++;
//        }
//        Map<String, ?> sRs = spRoute.getAll();
//        if (!sRs.isEmpty()) {
//            for (Map.Entry<String, ?> sR : sRs.entrySet()) {
//                if (!standardRouteNums.contains(sR.getKey())) {
//                    standardRouteNums.add(sR.getKey());
//                    sRoutes.add(gson.fromJson((String) sR.getValue(), StandardRoute.class));
//                }
//            }
//        }
//    }

    /**
     * 检查是否有正在等待的校准指令
     */
//    private void checkWaiting(){
//        if (waitingVerify > 0) {
//            executor.execute(checkIsReachRoute);
//            waitingVerify--;
//        }
//    }

    //检查是否到达某条路线的附近
    private Runnable checkIsReachRoute = new Runnable() {
        @Override
        public void run() {
            //每隔5秒检查本地是否有标准路线信息，是否没有正在进行校准的线程
            if (sRoutes.size() > 0 && suitableRoute == null) {
                logger.info("Verify 开始检查是否到达路线附近");
                //EventBus.getDefault().post("开始检查是否到达路线附近");
                //Log.d(TAG, "开始检查是否到达路线附近");
                int i = 0;//开始一次校准过程
                int size = sRoutes.size();
                while(true) {//循环检查每条路线的AB点，是否接近其中之一的半径
                    double ALng = sRoutes.get(i % size).getAlongitude();
                    double ALat = sRoutes.get(i % size).getAlatitude();
                    double ARadius = sRoutes.get(i % size).getAradius();
                    double BLng = sRoutes.get(i % size).getBlongtitde();
                    double BLat = sRoutes.get(i % size).getBlatitude();
                    double BRadius = sRoutes.get(i % size).getBradius();
                    if (getDistance(ALat, ALng) <= ARadius) {//若接近A点
                        suitableRoute = sRoutes.get(i % size);
                        endLat = BLat;
                        endLng = BLng;
                        //起点实测加出发时刻的GPS距离
                        carMeasure.setScarMeasure(getDistance(ALat, ALng) + suitableRoute.getSmeasure());
                        radius = BRadius;
                        logger.info("Verify到达起点A附近，开始校准");
                        logger.info("Verify A点经纬度：" + MyApp.locationMessageBody.getLongitude() + ", " + MyApp.locationMessageBody.getLatitude());
                        EventBus.getDefault().post("到达起点A附近");
                        Log.d(TAG, "到达起点A附近，开始校准");
                    } else if (getDistance(BLat, BLng) <= BRadius) {//若接近B点
                        suitableRoute = sRoutes.get(i % size);
                        endLat = ALat;
                        endLng = ALng;
                        radius = ARadius;
                        //起点实测加出发时刻的GPS距离
                        carMeasure.setScarMeasure(getDistance(BLat, BLng) + suitableRoute.getSmeasure());
                        logger.info("Verify到达起点B附近");
                        logger.info("Verify B点经纬度：" + MyApp.locationMessageBody.getLongitude() + ", " + MyApp.locationMessageBody.getLatitude());
                        EventBus.getDefault().post("到达起点B附近");
                        Log.d(TAG, "到达起点B附近");
                    } else {//若AB点均不满足则重新检查其他路线上的点
                        i++;
                        if (i < 0) {
                            i = 0;
                        }
                        continue;
                    }
                    //到达某一条路线上的点的附近，获取里程点
                    TaxiMeterDataAnalysis.newInstance().getMeterPoint(TaxiMeterDataAnalysis.VERIFY_ADJUST);
                    //开始检测是否到达校准的终点
                    executor.execute(checkIsReachEndPoint);
                    //记录校准过程的轨迹
                    isBeginRecordTrack = true;
                    executor.execute(recordTrack);
                    //保存校准起点的经纬度，速度，高度
                    carMeasure.setAlongitude(getDouble4(MyApp.locationMessageBody.getLongitude()));
                    carMeasure.setAlatitude(getDouble4(MyApp.locationMessageBody.getLatitude()));
                    carMeasure.setAaltitude(getDouble4(MyApp.locationMessageBody.getAltitude()));
                    carMeasure.setAspeed(getDouble4(MyApp.locationMessageBody.getSpeed()));
                    soundRing();
                    break;
                }
            }
        }
    };

    //记录校准期间的位置点
    private Runnable recordTrack = new Runnable() {
        @Override
        public void run() {
            while(isBeginRecordTrack) {//隔2秒记录一次
                delay(1000);
                synchronized (locationTracks) {
                    locationTracks.add(new LocationTrack(getDouble4(MyApp.locationMessageBody.getLongitude()),
                            getDouble4(MyApp.locationMessageBody.getLatitude()),
                            getDouble4(MyApp.locationMessageBody.getSpeed()),
                            TimeUtils.getStampTime()));
                }
            }
        }
    };
    //检测是否到达校准终点附近
    private Runnable checkIsReachEndPoint = new Runnable () {
        @Override
        public void run() {
            long timeBegin = TimeUtils.getStampTime();
            timeOut = suitableRoute.getSmeasure() * 360;
            logger.info("Verify检测是否到达校准终点附近");
            EventBus.getDefault().post("检测是否到达校准终点附近");
            //Log.d("Verify", "检测是否到达校准终点附近");

            //delay(10000);

            while(true) {
                if(getDistance(endLat, endLng) <= radius) {//规定时间内到达终点半径的范围,校准成功
                    //查询计价器里程点，结果通过回调passQueryMeterData()方法，保存到 endMeterPoint中
                    TaxiMeterDataAnalysis.newInstance().getMeterPoint(TaxiMeterDataAnalysis.VERIFY_ADJUST);
                    isBeginRecordTrack = false;
                    carMeasure.setScarMeasure(carMeasure.getScarMeasure() - getDistance(endLat, endLng));
                    soundRing();
                    logger.info("Verify到达终点附近");
                    logger.info("Verify终点经纬度：" + MyApp.locationMessageBody.getLongitude() + ", " + MyApp.locationMessageBody.getLatitude());
                    EventBus.getDefault().post("到达终点附近");
                    break;
                } else if ((TimeUtils.getStampTime() - timeBegin) > timeOut) {//若没有到达终点半径范围内，看是否超时
                    //十分钟之内还未到达终点半径的范围
                    setVerifyEnd("Verify 超时");
                    break;
                }
            }
        }
    };

    private void setVerifyEnd(String msg){
        //清除所选定的校准路线
        suitableRoute = null;
        //计价器里程点读取次数归零
        isFirstGetPoint = true;
        //停止记录位置点
        isBeginRecordTrack = false;
        //清空所有已记录的位置点
        locationTracks.clear();
        logger.info(msg);
        EventBus.getDefault().post(msg);
        //重新开始校准
        executor.execute(checkIsReachRoute);
    }

    @Override
    public void passQueryMeterData(SerialPortData.QueryMeterPointData queryMeterPointData) {
        if(isFirstGetPoint) {
            startMeterPoint = queryMeterPointData.getMeterPoint();
            logger.info("Verify startMeterPoint = " + startMeterPoint + "  isFirstGetPoint = " + isFirstGetPoint);
            isFirstGetPoint = false;
        } else {
            endMeterPoint = queryMeterPointData.getMeterPoint();
            taximeterMeasure = Double.parseDouble(df.format((endMeterPoint - startMeterPoint) * 1000));
            //开启线程处理校准结果
            executor.execute(finishVerify);
            logger.info("Verify endMeterPoint = " + endMeterPoint + "  isFirstGetPoint = " + isFirstGetPoint);
        }
    }
    //到达校准终点后处理数据
    private Runnable finishVerify = new Runnable() {
        @Override
        public void run() {
            carMeasure.setBlongtitde(getDouble4(MyApp.locationMessageBody.getLongitude()));
            carMeasure.setBlatitude(getDouble4(MyApp.locationMessageBody.getLatitude()));
            carMeasure.setBaltitude(getDouble4(MyApp.locationMessageBody.getAltitude()));
            carMeasure.setBspeed(getDouble4(MyApp.locationMessageBody.getSpeed()));
            //等待计价器返回里程点信息
            //carMeasure.setScarMeasure(Double.parseDouble(df.format(taximeterMeasure)));
//            while(true) {
//                delay(800);
//                if (taximeterMeasure != 0) {
//                    carMeasure.setScarMeasure(taximeterMeasure);
//                    isFirstGetPoint = 0;//查询里程次数复位
//                    break;
//                }
//            }
            double result = (taximeterMeasure - carMeasure.getScarMeasure()) / carMeasure.getScarMeasure();
            carMeasure.setScarMeasure(Double.parseDouble(df.format(carMeasure.getScarMeasure())));
//            if ((result >= -0.04) && (result <= 0.01)) {
//                carMeasure.setIsRegular("yes");
//            } else {
//                carMeasure.setIsRegular("no");
//            }
			carMeasure.setIsRegular("" + taximeterMeasure + " " + suitableRoute.getSmeasure() + " " + df.format(result * 100) + "%");
            logger.info("Verify校准完成 carMeasure:" + carMeasure.toString());
            EventBus.getDefault().post("校准完成");
            boolean flag = true;
            int totalTime = 15;
            try {
                JSONObject pg;
                synchronized (locationTracks) {
                    //Map<String, Object> m = new MessagePackaging(114).getReportSCarMeasureMap(carMeasure, locationTracks);
                    //logger.info("Verify校准完成map数据：" + m.toString());
                    String gps = calculateDistance(locationTracks);
                    String t = carMeasure.getIsRegular() + " " + gps;
                    carMeasure.setIsRegular(t);
                    pg = new JSONObject(JsonUtils.toGson(new MessagePackaging(114).getReportSCarMeasureMap(carMeasure, locationTracks)));
                    logger.info("GPS计算距离：" + gps + " carMeasure.getIsRegular:" + carMeasure.getIsRegular());
                }
                Log.d("VerifyMeterPoint", "开始上传:" + pg.toString());
                logger.info("Verify校准完成json数据：" + pg.toString());
                while (flag) {
                    if (totalTime > 0) {//每隔一秒上传一次，共15次
                        if (httpJson.getPostJson() == null) {
                            httpJson.HttpJsonPost(pg, MyApp.serverUrl);
                        } else {//上传成功，收到回复后
                            if (httpJson.getPostJson().getString("flag").equals("failure")) {//服务器出错
                                httpJson.setPostJson(null);
                                logger.info("Verify服务器出错");
                            } else if (httpJson.getPostJson().getString("flag").equals("success")){//上传成功，停止循环
                                flag = false;
                                httpJson.setPostJson(null);

                                delay((int) suitableRoute.getBradius() * 720);

                                setVerifyEnd("Verify上传成功");
                                sum++;
                                Log.d(TAG, "sum = " + sum);
                            }
                        }
                        delay(4000);
                        totalTime--;
                    } else {//上传15次后不成功，停止循环，保存校准数据到本地，删除标准路线
                        flag = false;
                        httpJson.setPostJson(null);
                        putVerifyResult(pg);
                        setVerifyEnd("Verify上传失败，保存到本地");
                    }
                }
            } catch (JSONException e) {
                logger.error(e);
                e.printStackTrace();
            }
        }
    };
    //上传本地存储的校准结果数据
    private Runnable uploadVerifyResult = new Runnable() {
        @Override
        public void run () {
            Map<String, ?> results = spResult.getAll();
            if (!results.isEmpty()) {
                for (Map.Entry<String, ?> r : results.entrySet()) {
                    try {
                        JSONObject js = JsonUtils.stringToJsonObject((String) r.getValue());
                        HttpJson httpJson = new HttpJson();
                        while (true) {
                            if (httpJson.getPostJson() == null) {
                                httpJson.HttpJsonPost(js, MyApp.serverUrl);
                            } else {//上传成功，收到回复后
                                if (httpJson.getPostJson().getString("flag").equals("failure")) {//服务器出错
                                    httpJson.setPostJson(null);
                                } else {//上传成功，停止循环
                                    removeVerifyResult(r.getKey());
                                    break;
                                }
                            }
                            delay(1000);
                        }
                    } catch (JSONException e) {
                        logger.error(e);
                        e.printStackTrace();
                    }
                }
            }
        }

    };

    /**
     * 根据经纬点计算距离
     * @param locationTracks 经纬点集合
     * @return 是或者否
     */
    private String calculateDistance(List<LocationTrack> locationTracks) {
        double claculateDistance = 0.0000000001;
        double calculateSpeed = 0.0000000001, s = 1852.0 / 3600.0;
        LocationTrack begin = locationTracks.get(0);
        for(LocationTrack lTracks : locationTracks) {
            claculateDistance += distance(begin.getLatitude(), begin.getLongitude(), lTracks.getLatitude(), lTracks.getLongitude());
            begin = lTracks;
            calculateSpeed += lTracks.getSpeed() * s;
        }
        calculateSpeed = calculateSpeed - locationTracks.get(0).getSpeed() * s;
        //System.out.println("计价器" + taximeterCount + "GPS" + claculateDistance + " 误差" + (taximeterCount - claculateDistance) / claculateDistance);
        return doubleTwo(claculateDistance * 1000) + " " + doubleTwo(calculateSpeed);
    }
    /**
     * 获取两点的距离
     * @param beginLat 开始点纬度
     * @param beginLng 开始点经度
     * @param endLat 结束点纬度
     * @param endLng 结束经度
     * @return 两点间距离 单位：千米
     */
    private double distance(double beginLat, double beginLng, double endLat, double endLng) {
        final double EARTH_RADIUS = 6378.137;//地球半径,单位千米
        double a = rad(beginLat) - rad(endLat);
        double b = rad(beginLng) - rad(endLng);
        double radLat1 = rad(beginLat);
        double radLat2 = rad(endLat);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b/2),2)));
        s = s * EARTH_RADIUS;
        return s;
    }
    private String doubleTwo(double d) {
        return new DecimalFormat("######0.00").format(d);
    }
    /**
     *  将检定校准的结果存储到本地
     */
    private void putVerifyResult(JSONObject pg) {
        editorResult.putString(TimeUtils.getStampTimeString(), pg.toString());
        editorResult.commit();
    }

    private void removeVerifyResult (String timeStamp) {
        editorResult.remove(timeStamp);
        editorResult.commit();
    }

    /**
     *  去掉已经校准完成的路线
     */
    private void removeHasVerified() {
        String num = Integer.toString(suitableRoute.getNum());
        if (!spRoute.getString(num, null).equals("")) {
            editorRoute.remove(num);
            editorRoute.commit();
        }
    }

    /**
     * 获取当前位置到指定地点的距离
     * @param endLat 指定纬度
     * @param endLng 指定经度
     * @return 两点间距离 单位：米
     */
    private double getDistance(double endLat, double endLng) {
        final double EARTH_RADIUS = 6378.137;//地球半径,单位千米
        double rightLng = MyApp.locationMessageBody.getLongitude();
        double rightLat = MyApp.locationMessageBody.getLatitude();
        double radLat1 = rad(rightLat);
        double radLat2 = rad(endLat);
        double a = radLat1 - radLat2;
        double b = rad(rightLng) - rad(endLng);

        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b/2),2)));
        s = s * EARTH_RADIUS;
        //s = Math.round(s * 10000) / 10000;
        return s * 1000;
    }

    /**
     *  将度转化为弧度
     * @param d
     * @return
     */
    private double rad(double d) {
        return d * Math.PI / 180.0;
    }
    /**
     * {@code double} 类型保存四位小数
     * @param p
     * @return
     */
    private double getDouble4(double p) {
        return Double.parseDouble(format.format(p));
    }
    /**
     * 延时
     * @param time 毫秒
     */
    private void delay(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            logger.error(e);
            e.printStackTrace();
        }
    }

    private void soundRing() {
        MediaPlayer mp = new MediaPlayer();
        mp.reset();
        try {
            mp.setDataSource(MyApp.context, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
            mp.prepare();
            mp.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}
